package com.cskaoyan.javase.oop3._3lambda._0introduction;

import com.sun.tracing.dtrace.FunctionName;

/**
 * Lambda 表达式是 JDK8 的一个新特性
 * 可以取代接口的匿名内部类,写出更简洁,更优雅的Java 代码。
 *
 * 如果说匿名内部类实际上是局部内部类的更进一步，简化了局部内部类
 * 那么Lambda就是匿名内部类更进一步，语法上更简洁了，代码更优雅了
 *      是高端的玩法，是人上人的玩法。
 *
 * 以上描述总体就说了三点：
 *      1.Lambda表达式仍然是局部内部类，是特殊的局部内部类
 *          仍然定义在局部位置。而且局部内部类的注意事项，也一样存在。
 *
 *      2.Lambda表达式在取代匿名内部类时，不是全部都取代，而是取代接口的匿名内部类，而类的匿名内部类Lambda表达式是不能取代的。
 *
 *      3.Lambda表达式是匿名内部类的更进一步，Lambda表达式得到的也不是一个类，而是一个对象，并且是接口的子类对象。
 *
 *
 *  1.使用Lambda表达式的前提:
 *      Lambda表达式虽然说是取代接口的匿名内部类，但也不是什么接口都能用Lambda表达式创建子类对象。
 *      Lambda表达式要求,接口当中,有且只有一个必须强制子类实现的抽象方法
 *      这种接口,在Java中被称之为"功能接口"(Functional Interface)
 *
 *      注解: @FunctionalInterface
 *      该注解写在接口声明的上面,用来检测该接口是否为功能接口
 *
 *      功能接口意味着,该接口可以用Lambda表达式,创建它的子类对象
 *
 *      思考:
 *          a.功能接口,仅有一个方法吗?
 *          不是,因为到了Java8以后,接口中可以定义默认方法和静态方法
 *              它们不需要子类实现
 *
 *          b.功能接口,仅有一个抽象方法吗?
 *          不是,某些抽象方法,并不需要子类强制实现
 *
 *          扩展: 什么样的抽象方法不需要普通子类强制实现呢?
 *          当一个抽象方法,可以用Object类当中的方法作为实现,那么就不需要强制子类实现
 *
 *          当然,实际开发中,绝大多数情况下,功能接口中当中就只有一个方法,就是抽象方法
 *
 *  2.Lambda表达式的语法:
 *   (形参列表) -> {
 *      // 方法体
 *    }
 *  解释:
 *      a."(形参列表)"表示功能接口中那个强制子类实现的抽象方法的形参列表,直接照抄下来即可
 *           如果抽象方法的形参为空,那这里也保持空着.
 *
 *      b."->" 由英文字符 横杠和大于号 组成,它是Lambda表达式的运算符,读作"goes to"
 *      c."{}"是功能接口中那个强制子类实现的抽象方法的,"重写方法体"
 *          也就是说这个大括号里面,表示重写抽象方法的方法体
 *          既然是方法体,大括号内部定义的是局部变量
 *      思考:
 *          1.Lambda表达式创建的功能接口子类对象,能不能定义自身独有成员?
 *              不能,因为"{}"是方法体,不是类体
 *          2.Lambda表达式为什么要求接口是功能接口?
 *              因为Lambda表达式的语法中,最多也必须重写一个抽象方法
 *              所以要求接口是功能接口
 *
 *  3.Lambda表达式的定义和使用
 *      直接在局部位置写上述语法,肯定会报错
 *      因为这时Lambda表达式所表示的子类对象类型是不确定的,Java是强类型语言,这种情况是不允许发生的
 *
 *      所以,这种情况下,就需要一些额外代码,来帮助编译器明确Lambda表达式所表示对象的具体类型
 *      这个过程在java中被称之为"Lambda表达式的类型推断"
 *
 *
 *  4.Lambda表达式的类型推断
 *      既然是推断,就需要额外信息,在代码当中,体现为
 *      根据上下文代码,做出类型推断
 *      主要的方式有以下:
 *          1.用功能接口的引用接收它,相当于父类引用指向子类对象
 *          2.(非常少见,了解)使用Lambda表达式类型推断的特殊语法:
 *          ((功能接口名)(Lambda表达式)).抽象方法名(实参)
 *          这种特殊语法,要求立刻使用Lambda表达式
 *
 *          3.借助方法来完成类型推断(最常用,实际开发中几乎只有这种类型推断)
 *              a.借助方法的形参数据类型完成类型推断(最最常用)
 *                   当形参数据类型是功能接口时
 *                   那么显然Lambda表达式就可以在方法实参中,直接表示创建该功能接口的子类对象
 *
 *              b.借助方法的返回值数据类型完成类型推断
 *                  当方法的返回值类型是功能接口时
 *                  那么显然Lambda表达式就可以在这个方法体中,直接作为功能接口的子类对象,作为返回值.
 *
 *
 * 以上,Lambda表达式的基本使用就完了.
 *
 * @since 16:37
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        new IDemo() {
            @Override
            public void test() {
                System.out.println("hello world!");
            }
        };
        IDemo id = () -> {
            System.out.println("hello world!");
        };
        id.test();

        ((IDemo) (() -> {
            System.out.println("我今天穿着西服,打着领带,学习Lambda表达式~");
        })).test();


        // Lambda表达式创建IDemo2的子类对象
        method((String str) -> {
            System.out.println(str);
            return 666;
        });

        IDemo3 id2 = method();
        System.out.println(id2.test(1, 1));
    }

    public static void method(IDemo2 id2) {
        System.out.println(id2.test("肠粉"));
    }

    public static IDemo3 method() {
        /*return new IDemo3() {
            @Override
            public String test(int num1, int num2) {
                return null;
            }
        };*/
        return (int num1, int num2) -> {
            System.out.println("sum = " + (num1 + num2));
            return "我今晚回去😪~~";
        };
    }
}

@FunctionalInterface
interface IDemo3 {
    String test(int num1, int num2);
}

@FunctionalInterface
interface IDemo2 {
    int test(String str);
}

@FunctionalInterface
interface IDemo {
    void test();
}

@FunctionalInterface
interface IA {
    void test();

    default void testDefault() {
    }

    static void testStatic() {
    }

    // equals方法可以用Object类的equals方法作为默认实现,所以它不强制子类实现
    boolean equals(Object o);

    int hashCode();
}